﻿#include "OperatorsCustomDataManager.h"

#include <QFileDialog>
#include <QMessageBox>
#include <QCollator>
#include <QTimer>
#include <QComboBox>

#include "GUIFrame/MainWindow.h"
#include "GUIFrame/PropertyWidget.h"
#include "OperatorsInterface/GraphEventOperator.h"
#include "OperatorsInterface/TreeEventOperator.h"
#include "GUIWidget/GUIDataSolidWidget.h"
#include "GUIWidget/GUIDataFluidWidget.h"
#include "GUIWidget/GUIDataInletWidget.h"
#include "GUIWidget/GUIDataOutletWidget.h"
#include "GUIWidget/GUIMonitorSamplingPointWidget.h"
#include "GUIWidget/GUICfgPhysicalSettingWidget.h"
#include "GUIWidget/GUICfgComputationSettingWidget.h"
#include "GUIWidget/GUIPost3DVisualizationWidget.h"
#include "GUIFrame/RenderWidget.h"
#include "FITK_Component/FITKWidget/FITKMdiArea.h"
#include "FITK_Component/FITKRenderWindowVTK/FITKGraph3DWindowVTK.h"
#include "FITK_Component/FITKRenderWindowVTK/FITKGraphRender.h"
#include "FITK_Component/FITKRenderWindowVTK/FITKGraphObjectVTK.h"

#include "FITK_Kernel/FITKAppFramework/FITKAppFramework.h"
#include "FITK_Kernel/FITKAppFramework/FITKGlobalData.h"
#include "FITK_Kernel/FITKAppFramework/FITKAppSettings.h"

#include "FITK_Interface/FITKInterfaceCustomData/FITKCDInterface.h"
#include "FITK_Interface/FITKInterfaceCustomData/FITKCDDataSolid.h"
#include "FITK_Interface/FITKInterfaceCustomData/FITKCDDataFluid.h"
#include "FITK_Interface/FITKInterfaceCustomData/FITKCDDataInlet.h"
#include "FITK_Interface/FITKInterfaceCustomData/FITKCDDataOutlet.h"
#include "FITK_Interface/FITKInterfaceCustomData/FITKCDMonitorSamplingPoint.h"

#include "FITK_Interface/FITKInterfaceIO/FITKCustomDataXMLWriter.h"

namespace ModelOper
{
    OperatorsCustomDataManager::OperatorsCustomDataManager()
    {
		timer = new QTimer(this);
		connect(timer, &QTimer::timeout, this, &OperatorsCustomDataManager::next);

		GUI::MainWindow* mainWindow = dynamic_cast<GUI::MainWindow*>(FITKAPP->getGlobalData()->getMainWindow());
		if (mainWindow == nullptr)return;

		connect(mainWindow, &GUI::MainWindow::actionFieldChanged, this, &OperatorsCustomDataManager::fieldChanged);
    }

    OperatorsCustomDataManager::~OperatorsCustomDataManager()
    {

    }

	QStringList getFilesByFilter(QString folder, QString ext)
	{
		QStringList filters;
		filters << ext;

		QDir directory(folder);
		directory.setNameFilters(filters);
		directory.setSorting(QDir::LocaleAware);

		// 按文件名排序
		auto dirList = directory.entryList(QDir::Files, QDir::SortFlag::Name);
		QCollator collator;
		collator.setNumericMode(true);
		std::sort(dirList.begin(), dirList.end(),
			[&collator](const QString& str1, const QString& str2)
			{
				return collator.compare(str1, str2) < 0;
			}
		);

		QStringList fileList;
		for (auto& s : dirList)
		{
			fileList.append(folder + "/" + s);
		}

		return fileList;
	}

    bool OperatorsCustomDataManager::execGUI()
    {
        GUI::MainWindow* mainWindow = dynamic_cast<GUI::MainWindow*>(FITKAPP->getGlobalData()->getMainWindow());
        if (mainWindow == nullptr)return false;
        GUI::PropertyWidget* propertyWidget = mainWindow->getPropertyWidget();
        if (propertyWidget == nullptr)return false;

		int objID = -1;
		this->argValue("objID", objID);
		
		if (_senderName == "actionEditSolid") 
        {
            auto pSolidManager = Interface::FITKCDInterface::getInstance()->getCDDataSolidManager();
            GUI::GUIDataSolidWidget* widget = new GUI::GUIDataSolidWidget(pSolidManager->getDataByID(objID), this);
            propertyWidget->setWidget(widget);
        }
        else if (_senderName == "actionEditFluid")
        {
			auto pFluidManager = Interface::FITKCDInterface::getInstance()->getCDDataFluidManager();
			GUI::GUIDataFluidWidget* widget = new GUI::GUIDataFluidWidget(pFluidManager->getDataByID(objID), this);
			propertyWidget->setWidget(widget);
        }
		else if (_senderName == "actionEditInlet")
		{
			auto pInletManager = Interface::FITKCDInterface::getInstance()->getCDDataInletManager();
			GUI::GUIDataInletWidget* widget = new GUI::GUIDataInletWidget(pInletManager->getDataByID(objID), this);
			propertyWidget->setWidget(widget);
		}
		else if (_senderName == "actionEditOutlet")
		{
			auto pOutletManager = Interface::FITKCDInterface::getInstance()->getCDDataOutletManager();
			GUI::GUIDataOutletWidget* widget = new GUI::GUIDataOutletWidget(pOutletManager->getDataByID(objID), this);
			propertyWidget->setWidget(widget);
		}
		else if (_senderName == "actionEditSamplingPoint")
		{
			auto pSamplingPointManager = Interface::FITKCDInterface::getInstance()->getCDSamplingPointManager();
			GUI::GUIMonitorSamplingPointWidget* widget = new GUI::GUIMonitorSamplingPointWidget(pSamplingPointManager, this);
			propertyWidget->setWidget(widget);
		}
		else if (_senderName == "actionEditPhysicalSetting")
		{
			GUI::GUICfgPhysicalSettingWidget* widget = new GUI::GUICfgPhysicalSettingWidget(this);
			propertyWidget->setWidget(widget);
		}
		else if (_senderName == "actionEditComputationSetting")
		{
			GUI::GUICfgComputationSettingWidget* widget = new GUI::GUICfgComputationSettingWidget(this);
			propertyWidget->setWidget(widget);
		}
		else if (_senderName == "actionEdit3DVisualization")
		{
			GUI::GUIPost3DVisualizationWidget* widget = new GUI::GUIPost3DVisualizationWidget(this);
			propertyWidget->setWidget(widget);
		}
		else if (_senderName == "actionExportCDXML")
		{
			//获取工作目录
			QString wk;
			AppFrame::FITKAppSettings* settings = FITKAPP->getAppSettings();
			if (settings)
				wk = settings->getWorkingDir();

			// 
			QFileDialog dlg(mainWindow, QObject::tr("Export XML File"), wk, "XML(*.xml)");
			dlg.setAcceptMode(QFileDialog::AcceptSave);  //保存模式
			dlg.show();

			//阻塞，否则键盘按键事件不能处理
			bool accept = false;
			QEventLoop loop;
			connect(&dlg, &QFileDialog::accepted, [&] {loop.quit(); accept = true;  });
			connect(&dlg, &QFileDialog::rejected, [&] {loop.quit(); accept = false;  });
			loop.exec();
			if (!accept) return false;

			QStringList files = dlg.selectedFiles(); //选择的文件名称
			if (files.isEmpty()) return false;
			QString fileName = files.at(0);
			if (fileName.isEmpty())
				return false;
			if (!fileName.toLower().endsWith(".xml"))
				fileName += ".xml";
			this->setArgs("FileName", fileName);
		}
		else if (_senderName == "actionCDSolve")
		{
		}
		else if (_senderName == "actionImportResult")
		{
			//获取工作目录
			QString wk;
			AppFrame::FITKAppSettings* settings = FITKAPP->getAppSettings();
			if (settings)
				wk = settings->getWorkingDir();

			// 
			QFileDialog dlg(mainWindow, QObject::tr("Open Result Directory"), wk);
			dlg.setFileMode(QFileDialog::Directory);
			dlg.show();

			//阻塞，否则键盘按键事件不能处理
			bool accept = false;
			QEventLoop loop;
			connect(&dlg, &QFileDialog::accepted, [&] {loop.quit(); accept = true;  });
			connect(&dlg, &QFileDialog::rejected, [&] {loop.quit(); accept = false;  });
			loop.exec();
			if (!accept) return false;

			// 将output文件夹内的所有vtk文件加入后处理队列中
			QStringList files = dlg.selectedFiles(); //选择的文件名称
			if (files.isEmpty()) return false;
// 			QDir directory(files[0]);
// 			QStringList filters;
// 			filters << "*.vtk";
// 			directory.setNameFilters(filters);
// 			directory.setSorting(QDir::LocaleAware);
// 
// 			// 按文件名排序
// 			auto dirList = directory.entryList(QDir::Files, QDir::SortFlag::Name);
// 			QCollator collator;
// 			collator.setNumericMode(true);
// 			std::sort(dirList.begin(), dirList.end(),
// 				[&collator](const QString& str1, const QString& str2)
// 				{
// 					return collator.compare(str1, str2) < 0;
// 				}
// 			);
// 
// 			QStringList fileList;
// 			for (auto& s : dirList)
// 			{
// 				fileList.append(files[0] + "/" + s);
// 			}

			this->setArgs("ResultFiles", getFilesByFilter(files[0], "*.vtk"));
			this->setArgs("ResultFiles_stl", getFilesByFilter(files[0], "*.stl"));
		}

        return true;
    }

    bool OperatorsCustomDataManager::execProfession()
    {
        GUI::MainWindow* mainWindow = dynamic_cast<GUI::MainWindow*>(FITKAPP->getGlobalData()->getMainWindow());
        if (mainWindow == nullptr)return false;
        GUI::PropertyWidget* propertyWidget = mainWindow->getPropertyWidget();
        if (propertyWidget == nullptr)return false;

		auto treeOper = Core::FITKOperatorRepo::getInstance()->getOperatorT<EventOper::TreeEventOperator>("ModelTreeEvent");
		if (treeOper == nullptr) return false;

		EventOper::GraphEventOperator* graphOper = FITKOPERREPO->getOperatorT<EventOper::GraphEventOperator>("GraphPreprocess");
		if (graphOper == nullptr)return false;

		int objID = -1;
		this->argValue("objID", objID);

		auto pSolidManager = Interface::FITKCDInterface::getInstance()->getCDDataSolidManager();
		auto pFluidManager = Interface::FITKCDInterface::getInstance()->getCDDataFluidManager();
		auto pInletManager = Interface::FITKCDInterface::getInstance()->getCDDataInletManager();
		auto pOutletManager = Interface::FITKCDInterface::getInstance()->getCDDataOutletManager();

		if (_senderName == "actionAddSolid")
		{
			auto pSolidItem = new Interface::FITKCDDataSolid();
            pSolidItem->setDataObjectName("solid_" + QString::number(pSolidManager->getDataCount()));

            pSolidManager->appendDataObj(pSolidItem);
		}
        else if (_senderName == "actionAddFluid")
		{
			auto pFluidItem = new Interface::FITKCDDataFluid();
            pFluidItem->setDataObjectName("fluid_" + QString::number(pFluidManager->getDataCount()));

            pFluidManager->appendDataObj(pFluidItem);
		}
		else if (_senderName == "actionAddInlet")
		{
			auto pInletItem = new Interface::FITKCDDataInlet();
            pInletItem->setDataObjectName("inlet_" + QString::number(pInletManager->getDataCount()));

            pInletManager->appendDataObj(pInletItem);
		}
		else if (_senderName == "actionAddOutlet")
		{
			auto pOutletItem = new Interface::FITKCDDataOutlet();
            pOutletItem->setDataObjectName("outlet_" + QString::number(pOutletManager->getDataCount()));

            pOutletManager->appendDataObj(pOutletItem);
		}
        else if (_senderName == "actionDelSolid" || _senderName == "actionDelFluid" || _senderName == "actionDelInlet" || _senderName == "actionDelOutlet")
        {
            if (QMessageBox::question(nullptr, "Delete", "Are u sure to delete?") == QMessageBox::Yes)
            {
				if (_senderName == "actionDelSolid")
					pSolidManager->removeDataObj(pSolidManager->getDataByID(objID));
                else if (_senderName == "actionDelFluid")
                    pFluidManager->removeDataObj(pFluidManager->getDataByID(objID));
				else if (_senderName == "actionDelInlet")
                    pInletManager->removeDataObj(pInletManager->getDataByID(objID));
				else if (_senderName == "actionDelOutlet")
                    pOutletManager->removeDataObj(pOutletManager->getDataByID(objID));

                propertyWidget->init();
            }
        }
		else if (_senderName == "actionExportCDXML")
		{
			QString fileName;
			this->argValue<QString>("FileName", fileName);

			Interface::FITKCustomDataXMLWriter* tempIo = new Interface::FITKCustomDataXMLWriter();
			tempIo->setFileName(fileName);
			tempIo->createContent();
			tempIo->run();
			tempIo->saveFile();
		}
		else if (_senderName == "actionCDSolve")
		{
			if (QMessageBox::question(nullptr, "Solve", "Are u sure to start?") == QMessageBox::No)
			{
				return false;
			}

			//获取工作目录
			QString wk;
			AppFrame::FITKAppSettings* settings = FITKAPP->getAppSettings();
			if (settings)
				wk = settings->getWorkingDir();

			if (wk.isEmpty())
			{
				return false;
			}

			// 生成求解器配置文件
			QString fileName = wk + "/Case.xml";
			Interface::FITKCustomDataXMLWriter* tempIo = new Interface::FITKCustomDataXMLWriter();
			tempIo->setFileName(fileName);
			tempIo->createContent();
			tempIo->run();
			tempIo->saveFile();

			// 调用求解器
			QString ps = "cd " + wk + "; ./PK_Hydro.exe ./Case.xml";
			QString sCmd = "powershell.exe -Command \"" + ps + "\"";
			auto ss = sCmd.toLocal8Bit().toStdString();
			auto s = ss.c_str();
			system(s);

			// 显示结果

		}
		else if (_senderName == "actionImportResult")
		{
			QStringList fileList;
			this->argValue<QStringList>("ResultFiles", fileList);

			QStringList stlList;
			this->argValue<QStringList>("ResultFiles_stl", stlList);

			// 获取可视化窗口。
 			Comp::FITKGraph3DWindowVTK* graphWidget = getGraphWidget();
			graphWidget->setCustomDataList(fileList, stlList);

			mainWindow->getFieldWidget()->clear();
			mainWindow->getFieldWidget()->addItems(graphWidget->getFieldList());
		}
		else if (_senderName == "actionFirstResult")
		{
 			Comp::FITKGraph3DWindowVTK* graphWidget = getGraphWidget();
			graphWidget->setResultFrame(0);
		}
		else if (_senderName == "actionPreviousResult")
		{
			Comp::FITKGraph3DWindowVTK* graphWidget = getGraphWidget();
			if (graphWidget->getResultFrame() != 0)
			{
				graphWidget->setResultFrame(graphWidget->getResultFrame() - 1);
			}
		}
		else if (_senderName == "actionRunResult")
		{
			timer->start();
		}
		else if (_senderName == "actionNextResult")
		{
 			next();
		}
		else if (_senderName == "actionLastResult")
		{
 			Comp::FITKGraph3DWindowVTK* graphWidget = getGraphWidget();
			graphWidget->setResultFrame(graphWidget->getCustomDataList().size() - 1);
		}

        treeOper->updateTree();
        return true;
    }

	void OperatorsCustomDataManager::next()
	{
		Comp::FITKGraph3DWindowVTK* graphWidget = getGraphWidget();
		if (graphWidget->getResultFrame() != graphWidget->getCustomDataList().size() - 1)
		{
			graphWidget->setResultFrame(graphWidget->getResultFrame() + 1);
		}
		else
		{
			timer->stop();
		}
	}

	void OperatorsCustomDataManager::fieldChanged(QString s)
	{
		Comp::FITKGraph3DWindowVTK* graphWidget = getGraphWidget();
		graphWidget->setResultField(s);
	}

	Comp::FITKGraph3DWindowVTK* OperatorsCustomDataManager::getGraphWidget()
	{
		// 获取主窗口。
		GUI::MainWindow* mainWindow = FITKAPP->getGlobalData()->getMainWindowT<GUI::MainWindow>();
		if (!mainWindow)
		{
			return nullptr;
		}

		// 获取可视化区。
		GUI::RenderWidget* renderWidget = mainWindow->getRenderWidget();
		if (!renderWidget)
		{
			return nullptr;
		}

		Comp::FITKMdiArea* mdiArea = renderWidget->getMdiArea();
		if (!mdiArea)
		{
			return nullptr;
		}

		// 获取当前窗口，尝试转换为三维窗口。
		QWidget* w = mdiArea->getCurrentWidget();
		Comp::FITKGraph3DWindowVTK* graphWidget = dynamic_cast<Comp::FITKGraph3DWindowVTK*>(w);

		return graphWidget;
	}
}
